package com.atguigu.tingshu.user.service.impl;

import cn.binarywang.wx.miniapp.api.WxMaService;
import com.alibaba.fastjson.JSONObject;
import com.atguigu.tingshu.album.client.TrackInfoFeignClient;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.kafka.KafkaMessagePojo;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.order.OrderInfo;
import com.atguigu.tingshu.model.user.*;
import com.atguigu.tingshu.order.client.OrderInfoFeignClient;
import com.atguigu.tingshu.user.config.WxLoginConfig;
import com.atguigu.tingshu.user.dao.UserCollectDao;
import com.atguigu.tingshu.user.mapper.*;
import com.atguigu.tingshu.user.service.UserInfoService;
import com.atguigu.tingshu.user.service.VipServiceConfigService;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.atguigu.tingshu.vo.user.UserSubscribeVo;
import com.baomidou.mybatisplus.core.assist.ISqlRunner;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.xiaoymin.knife4j.core.util.Assert;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.security.jwt.Jwt;
import org.springframework.security.jwt.JwtHelper;
import org.springframework.security.jwt.crypto.sign.RsaSigner;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.security.interfaces.RSAPrivateKey;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements UserInfoService {

  @Autowired
  private UserInfoMapper userInfoMapper;

  @Autowired
  private WxLoginConfig wxLoginConfig;

  @Autowired
  private RestTemplate restTemplate;

  @Autowired
  private WxMaService wxMaService;

  @Autowired
  public RSAPrivateKey guiGuPrivateKey;

  @Autowired
  private RedisTemplate redisTemplate;

  @Autowired
  private RabbitTemplate rabbitTemplate;

  @Autowired
  private OrderInfoFeignClient orderInfoFeignClient;

  @Autowired
  private TrackInfoFeignClient trackInfoFeignClient;

  @Autowired
  private UserVipServiceMapper userVipServiceMapper;

  @Autowired
  private VipServiceConfigMapper vipServiceConfigMapper;

  /**
   * 订单成功支付后 更新用户的购买记录
   * @param orderNo
   */
  @Override
  public void updateUserPaidInfo(String orderNo) {
    //查询订单的数据
    OrderInfo orderInfo = orderInfoFeignClient.getOrderInfo(orderNo);
    if(orderInfo == null){
      return;
    }
    //分场景处理
    switch (orderInfo.getItemType()){
      case "1001"->{
        //专辑订单: 记录用户购买了哪个专辑
        insertUserPaidAlbum(orderInfo);
      }
      case "1002"->{
        //声音订单: 记录用户购买了哪些声音
        insertUserPaidTrack(orderInfo);
      }
      case "1003"->{
        //会员订单: 修改用户的vip状态 + vip的有效时间
        updateUserVipStatus(orderInfo);
      }
    }
  }

  /**
   * vip订单: 记录用户购买的VIP订单 user_vip_service
   * @param orderInfo
   */
  private void updateUserVipStatus(OrderInfo orderInfo) {
    //获取订单号
    String orderNo = orderInfo.getOrderNo();
    //查看这个订单是否被处理过
    Long count =
            userVipServiceMapper.selectCount(
                    new LambdaQueryWrapper<UserVipService>()
                            .eq(UserVipService::getOrderNo, orderNo));
    if(count > 0){
      return;
    }
    //查询用户信息
    UserInfo userInfo = getById(orderInfo.getUserId());
    //查询本次购买的vip的服务详细信息
    VipServiceConfig vipServiceConfig =
            vipServiceConfigMapper.selectById(orderInfo.getOrderDetailList().get(0).getItemId());
    //获取vip的时长,服务月数
    Integer serviceMonth = vipServiceConfig.getServiceMonth();
    //修改用户的vip状态
    userInfo.setIsVip(1);
    //获取旧的用户的vip过期时间
    DateTime dateTime = null;
    Date vipExpireTime = userInfo.getVipExpireTime();
    // 如果vip已过期 则从现在开始计算过期时间
    if(vipExpireTime.getTime() < new Date().getTime()){
      //从今天开始算
      dateTime = new DateTime().plusMonths(serviceMonth);
    }else{
      //从旧的过期时间开始算
      dateTime = new DateTime(vipExpireTime).plusMonths(serviceMonth);
    }
    userInfo.setVipExpireTime(dateTime.toDate());
    //修改用户信息
    if(!updateById(userInfo)){
      throw new GuiguException(201, "续费vip失败!");
    }
    //记录用户的vip充值明细
    UserVipService userVipService = new UserVipService();
    userVipService.setOrderNo(orderNo);
    userVipService.setUserId(userInfo.getId());
    userVipService.setStartTime(new Date());
    //到期时间
    userVipService.setExpireTime(userInfo.getVipExpireTime());
    if(userVipServiceMapper.insert(userVipService) <= 0){
      throw new GuiguException(201, "记录vip充值失败");
    }
  }

  /**
   * 声音订单: 记录用户购买了哪个声音 user_paid_track
   * @param orderInfo
   */
  private void insertUserPaidTrack(OrderInfo orderInfo) {
    //获取订单号
    String orderNo = orderInfo.getOrderNo();
    //查看这个订单是否被处理过
    Long count =
            userPaidTrackMapper.selectCount(
                    new LambdaQueryWrapper<UserPaidTrack>()
                            .eq(UserPaidTrack::getOrderNo, orderNo));
    if(count > 0){
      return;
    }
    //查询声音的详情
    Long albumId = trackInfoFeignClient
            .getTrackInfo(orderInfo.getOrderDetailList().get(0).getItemId()).getAlbumId();
    // 遍历插入声音的购买记录
    orderInfo.getOrderDetailList().stream().forEach(orderDetail -> {
      UserPaidTrack userPaidTrack = new UserPaidTrack();
      userPaidTrack.setOrderNo(orderNo);
      userPaidTrack.setUserId(orderInfo.getUserId());
      userPaidTrack.setAlbumId(albumId);
      userPaidTrack.setTrackId(orderDetail.getItemId());
      if(userPaidTrackMapper.insert(userPaidTrack) <= 0){
        throw new GuiguException(201, "保存声音购买记录失败");
      }
    });
    
  }

  /**
   * 专辑订单: 记录用户购买了哪张专辑 user_paid_album
   * @param orderInfo
   */
  private void insertUserPaidAlbum(OrderInfo orderInfo) {
    //获取订单号
    String orderNo = orderInfo.getOrderNo();
    //查看是否已经处理过这个订单
    Long count =
            userPaidAlbumMapper.selectCount(
                    new LambdaQueryWrapper<UserPaidAlbum>()
                            .eq(UserPaidAlbum::getOrderNo, orderNo));
    if(count > 0){
      return;
    }
    //获取专辑id
    Long albumId = orderInfo.getOrderDetailList().get(0).getItemId();
    //获取用户id
    Long userId = orderInfo.getUserId();
    //DO初始化
    UserPaidAlbum userPaidAlbum = new UserPaidAlbum();
    userPaidAlbum.setOrderNo(orderNo);
    userPaidAlbum.setUserId(userId);
    userPaidAlbum.setAlbumId(albumId);
    if(userPaidAlbumMapper.insert(userPaidAlbum) <= 0){
      throw new GuiguException(201, "记录专辑购买失败!");
    }
  }

  /**
   * 使用微信登录小程序
   * @param code
   * @return null
   */
  @Override
  public JSONObject wxLogin(String code) {
    // 校验code是否为空
    if (StringUtils.isEmpty(code)){
      throw new GuiguException(201, "参数错误");
    }
    // 拼接参数到请求对象的URL中
    String url = wxLoginConfig.getUrl();
    url +=   "&appid=" + wxLoginConfig.getAppId()
            + "&secret=" + wxLoginConfig.getAppSecret()
            + "&js_code=" + code;
    // 向微信服务器发送get请求 获取openid
    String resultString = restTemplate.getForObject(url, String.class);
    // 解析结果
    JSONObject result = JSONObject.parseObject(resultString);
    // 从结果中获取openid
    String openid = result.get("openid").toString();
    // 方案二 使用微信官方提供的SDK发请求
    // String openid = wxMaService.jsCode2SessionInfo(code).getOpenid();

    // 在数据库 tingshu_user user_info表中 根据这个openid 查询用户信息
    UserInfo userInfo = this.getOne(new LambdaQueryWrapper<UserInfo>()
                                       .eq(UserInfo::getWxOpenId, openid)
                                       .last("limit 1"));
    if (userInfo == null){
      // 如果用户信息不存在创建初始化一个新用户信息
      userInfo = new UserInfo();
      userInfo.setWxOpenId(openid);
      userInfo.setNickname("初始用户" + System.currentTimeMillis());
      userInfo.setAvatarUrl("http://192.168.200.130:9000/atguiguts/f82e27a81fba4c6db93d02a653c59466");
      this.save(userInfo);
      // 优化 发送消息完成账户的初始化
      rabbitTemplate.convertAndSend("user_and_account_exchange",
                                    "user.account",
                                    userInfo.getId().toString());
    } else {
      // todo 广播数据预热
    }
    // 自定义载荷
    JSONObject payload = new JSONObject();
    // 用户id
    payload.put("userId", userInfo.getId());
    // 用户VIP信息
    payload.put("isVip", userInfo.getIsVip());
    // 每个jwt的唯一标识
    String jti = IdWorker.get32UUID();
    payload.put("jti", jti);
    // 使用私钥生成jwt的令牌 并使用RSA算法对头部和载荷进行签名 参数1:载荷数据 参数2:签名(盐) RSA是一种非对称加密算法
    Jwt encode = JwtHelper.encode(payload.toString(), new RsaSigner(guiGuPrivateKey));
    // 生成JWT类型的token
    String token = encode.getEncoded();
    //只记录用户最后一次登录: 记录一下用户的登录状态: 1.MySQL 2.IOC的容器 3.第三方组件(redis所有的校验服务都能访问)
    String key = "User_Login_Id:" + userInfo.getId();
    redisTemplate.opsForValue().set(key, token, 10, TimeUnit.DAYS); // 设置token的过期时间为一天
    // 将token放入结果中返回
    JSONObject res = new JSONObject();
    res.put("token", token);
    // 返回
    return res;
  }
  /**
   * 非对称加密
   * 1.每次加密的结果不一样: 校验的时候不再使用的是等于!!!!!! 直接进行匹配!!!
   * 2.生成和校验时候使用的盐不一样:
   * 	a.生成token使用的盐: A--私钥(私有不暴露)
   * 	b.校验token使用的盐: B---公钥(公开的)
   * 	公钥+私钥=一对钥匙,必须成对存在,公钥给校验方, 私钥自己保留不要暴露
   */

  @Override
  public Object getUserInfo() {
    //获取用户id
    Long userId = AuthContextHolder.getUserId();
    //查询用户的信息
    UserInfo userInfo = this.getById(userId);
    //转换为vo
    UserInfoVo userInfoVo = new UserInfoVo();
    BeanUtils.copyProperties(userInfo, userInfoVo);
    userInfo.setWxOpenId("");
    //返回
    return userInfoVo;
  }

  @Autowired
  private UserPaidAlbumMapper userPaidAlbumMapper;
  /**
   * 查询用户是否购买过专辑的记录
   * @param userId
   * @param albumId
   * @return
   */
  @Override
  public Boolean getUserPaidAlbum(Long userId, Long albumId) {
    Long count = userPaidAlbumMapper.selectCount(new LambdaQueryWrapper<UserPaidAlbum>()
                                                     .eq(UserPaidAlbum::getUserId, userId)
                                                     .eq(UserPaidAlbum::getAlbumId, albumId));

    return count > 0;
  }

  @Autowired
  private UserPaidTrackMapper userPaidTrackMapper;
  /**
   * 查询用户购买过声音的记录
   * @param userId
   * @param albumId
   * @return
   */
  @Override
  public Map<String, Object> getUserPaidTrack(Long userId, Long albumId) {
    // 查询某用户在某专辑中购买过的付费的声音列表
    List<UserPaidTrack> userPaidTracks
            = userPaidTrackMapper.selectList(new LambdaQueryWrapper<UserPaidTrack>()
                                     .eq(UserPaidTrack::getUserId, userId)
                                     .eq(UserPaidTrack::getAlbumId, albumId));
    // 转成map返回 有键的就是买过的声音
    return userPaidTracks.stream().collect(Collectors.toMap(
            key -> key.getTrackId().toString(),
            val -> 0
    ));
  }

  // @Autowired
  // private UserCollectDao userCollectDao;

  @Autowired
  private MongoTemplate mongoTemplate;

  @Autowired
  private KafkaTemplate kafkaTemplate;
  /**
   * 用户收藏当前声音
   * @param trackId
   * @return
   */
  @Override
  public void collect(Long trackId) {
    // 获取用户id
    Long userId = AuthContextHolder.getUserId();
    // 集合名称
    String collectName = "user_collect_" + userId;
    // 查询该用户的某条声音的收藏记录
    UserCollect userCollect = mongoTemplate.findOne(
            Query.query(Criteria
                                .where("userId").is(userId)
                                .and("trackId").is(trackId)
                                // .and("isDeleted").is(0)
            ),
            UserCollect.class,
            collectName
    );
    // 删除该用户的某条声音的收藏记录 mongo不适合做删除操作
    // mongoTemplate.remove(
    //         Query.query(Criteria
    //                             .where("userId").is(userId)
    //                             .and("trackId").is(trackId)),
    //         collectName);
    // 如果是第一次收藏
    if (userCollect == null){
      userCollect = new UserCollect();
      // 属性赋值
      userCollect.setId(IdWorker.get32UUID());
      userCollect.setUserId(userId);
      userCollect.setTrackId(trackId);
      userCollect.setCreateTime(new Date());
      userCollect.setIsDeleted(0);
      //记录收藏行为
      TrackInfo trackInfo = trackInfoFeignClient.getTrackInfo(trackId);
      //发送行为消息
      KafkaMessagePojo kafkaMessagePojo = new KafkaMessagePojo();
      kafkaMessagePojo.setAlbumId(trackInfo.getAlbumId());
      kafkaMessagePojo.setType("collect");
      kafkaTemplate.send("tingshu_stream_rank_topic", JSONObject.toJSONString(kafkaMessagePojo));
    } else {
      // 如果已经有收藏的记录 则取消收藏
      userCollect.setIsDeleted(userCollect.getIsDeleted().equals(0)?1:0);
    }
    // 执行插入数据
    mongoTemplate.save(userCollect, collectName);
  }

  /**
   * 查询用户是否收藏当前声音
   * @param trackId
   * @return
   */
  @Override
  public Object isCollect(Long trackId) {
    // 获取用户id
    Long userId = AuthContextHolder.getUserId();
    // 集合名称
    String collectName = "user_collect_" + userId;
    // 查询该用户的某条声音的收藏记录
    long count = mongoTemplate.count(
            Query.query(Criteria
                                .where("userId").is(userId)
                                .and("trackId").is(trackId)
                                .and("isDeleted").is(0)
            ),
            UserCollect.class,
            collectName
    );
    return count > 0;
  }



  /**
   * 查询用户是否订阅当前专辑
   * @param albumId
   * @return
   */
  @Override
  public Object isSubscribe(Long albumId) {
    // 获取用户id
    Long userId = AuthContextHolder.getUserId();
    // 集合名称
    String collectName = "user_subscribe_" + userId;
    // 查询该用户的某条声音的收藏记录
    UserSubscribe userSubscribe = mongoTemplate.findOne(
            Query.query(Criteria
                                .where("userId").is(userId)
                                .and("albumId").is(albumId)
                                .and("isDeleted").is(0)
            ),
            UserSubscribe.class,
            collectName
    );
    return userSubscribe != null;
  }

}
